package flca.projbuilder.util;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

import com.google.common.io.Files;

import flca.projbuilder.dto.TemplateDto;

/**
 * this class will actually execute the copy and substitute actions
 * 
 * @author nly36776
 * 
 */
public class ProjbuildHelper {

	private TemplateDto template;
	private ZipFile zipfile;
	private Map<String, String> subsFromTos;

	public void doGenerate(TemplateDto aTemplate) {
		this.template = aTemplate;
		expandFromTos();
		showFromTos();
		zipfile = getZipFile();
		Enumeration<?> entries = zipfile.entries();
		while (entries.hasMoreElements()) {
			processEntry(zipfile, (ZipEntry) entries.nextElement());
		}
	}

	/**
	 * also add the UPPERCASE and Camelcase variant to the list of from-to's
	 */
	private void expandFromTos() {
		subsFromTos = new HashMap<>();
		for (String key : this.template.getFromTos().keySet()) {
			subsFromTos.put(key, this.template.getFromTos().get(key));
			subsFromTos.put(key.toUpperCase(), this.template.getFromTos().get(key).toUpperCase());
			subsFromTos.put(capName(key), capName(this.template.getFromTos().get(key)));
		}
	}

	private void showFromTos() {
		System.out.println("Generating target project(s) using the following FromTo values:");
		for (String key : this.subsFromTos.keySet()) {
			System.out.println(key + " => " + this.subsFromTos.get(key));
		}
	}

	private ZipFile getZipFile() {
		try {
			ZipFile zip = new ZipFile(template.getZipSource());
			return zip;
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	private void processEntry(ZipFile aZip, ZipEntry aEntry) {
		if (aEntry.isDirectory()) {
			processZipFolder(aZip, aEntry);
		} else {
			processZipEntry(aZip, aEntry);
		}
	}

	private void processZipFolder(ZipFile aZipFile, ZipEntry aZipEntry) {
		try {
			String tofileName = generateToFile(aZipEntry.getName(), targetDir());
			new File(tofileName).mkdirs();
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	private void processZipEntry(ZipFile aZipFile, ZipEntry aZipEntry) {
		try {
			String tofileName = generateToFile(aZipEntry.getName(), targetDir());
			String todir = new File(tofileName).getParent();
			new File(todir).mkdirs();
			String source = ZipHelper.readZipEntry(aZipFile, aZipEntry);
			if (isBinary(source)) {
				copyBinaryFile(tofileName, aZipFile, aZipEntry);
			} else {
				substituteAndWriteTargerfile(tofileName, source);
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	private void substituteAndWriteTargerfile(String tofileName, String source) throws IOException {
		String target = substituteFromToValues(source, subsFromTos, false);
		System.out.println("generating " + tofileName);
		Files.write(target.getBytes(), new File(tofileName));
	}

	private void copyBinaryFile(String tofileName, ZipFile aZipFile, ZipEntry aZipEntry) {
		FileOutputStream os = null;
		try {
			os = new FileOutputStream(new File(tofileName));
			IOUtils.copy(aZipFile.getInputStream(aZipEntry), os);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (os != null) {
				try {os.close();}catch (IOException e) {}
			}
		}
		
	}
	
 	private String generateToFile(String aFromFileName, String aTargetDir) {
		String result = aTargetDir + "/" + aFromFileName;
		result = substituteFromToValues(result, subsFromTos, true);
		return result;
	}

	private String substituteFromToValues(String aSource, Map<String, String> aFromToValues, boolean aPath) {
		String result = aSource;

		if (aFromToValues != null) {
			for (String fromstr : aFromToValues.keySet()) {
				String tostr = aFromToValues.get(fromstr);
				result = StringUtils.replace(result, fromstr, tostr);
			}
		}

		return result;
	}

	private String targetDir() {
		return this.template.getToDir();
	}

	public String capName(String name) {
		if (name.length() == 0)
			return name;
		else
			return (name.substring(0, 1).toUpperCase() + name.substring(1)).replace(" ", "");
	}

	private static boolean isBinary(String aSource) {
		char chars[] = aSource.toCharArray();
		for (char c : chars) {
			if ((int) c == 0) {
				return true;
			}
		}
		return false;
	}

	// private String subsDelim(String aSource) {
	// return StringUtils.replace(aSource, "\\", "/");
	// }

}
